﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class represents a complex inverse matrix.
    /// </summary>
    [Serializable]
    public class ComplexInverseMatrix : ComplexMatrix
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ComplexInverseMatrix"/> class.
        /// </summary>
        /// <param name="dimension">The dimension of the complex matrix.</param>
        public ComplexInverseMatrix(int dimension)
            : base(dimension, dimension)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ComplexInverseMatrix"/> class.
        /// </summary>
        /// <param name="matrix">The complex matrix to clone.</param>
        public ComplexInverseMatrix(ComplexInverseMatrix matrix)
            : base(matrix)
        {
        }

        /// <summary>
        /// Computes the inverse of a complex matrix.
        /// </summary>
        /// <param name="matrix">The matrix from which the inverse should be computed.</param>
        /// <returns>The computed inverse of the specified matrix.</returns>
        public static ComplexInverseMatrix FromMatrix(ComplexMatrix matrix)
        {
            if (matrix == (ComplexMatrix) null)
            {
                throw new ArgumentNullException("matrix");
            }

            if (matrix.Rows != matrix.Columns)
            {
                throw new ArgumentException("The specified matrix has to be a square matrix.");
            }

            ComplexNumber[,] tempuri = ComputeComplexMatrixInverse((ComplexNumber[,]) matrix.MatrixData.Clone(),
                                                                   matrix.Rows);
            ComplexInverseMatrix result = new ComplexInverseMatrix(matrix.Rows);

            for (int i = 0; i < result.Rows; i++)
            {
                for (int j = 0; j < result.Columns; j++)
                {
                    result.SetValueAtPosition(i, j, tempuri[i, j]);
                }
            }

            return result;
        }

        /// <summary>
        /// Computes the inverse of a complex matrix.
        /// </summary>
        /// <param name="a">The two dimensional array, which includes the matrix data.</param>
        /// <param name="n">The dimension of the matrix.</param>
        /// <returns>A two dimensional array of complex numbers, which represents the inverse of the matrix.</returns>
        private static ComplexNumber[,] ComputeComplexMatrixInverse(ComplexNumber[,] a, int n)
        {
            int j;
            ComplexNumber tempuri;
            ComplexNumber[,] inverse = new ComplexNumber[n,n];

            for (int i = 0; i < n; i++)
            {
                for (j = 0; j < n; j++)
                {
                    inverse[i, j] = new ComplexNumber(0);
                }

                inverse[i, i] = new ComplexNumber(1);
            }

            for (int k = 0; k < n; k++)
            {
                tempuri = a[k, k];

                if (tempuri.Abs == 0.0)
                {
                    for (j = k + 1; j < n; j++)
                    {
                        tempuri = a[j, k];

                        if (tempuri.Abs != 0.0)
                        {
                            break;
                        }
                    }

                    if (tempuri.RealNumberPart == 0.0)
                    {
                        throw new ConvergenceException("The algorithm is not able to compute the inverse matrix.");
                    }

                    SwapRows(a, j, k);
                    SwapRows(inverse, j, k);
                }

                for (j = 0; j < n; j++)
                {
                    if (j > k)
                    {
                        a[k, j] /= tempuri;
                    }

                    inverse[k, j] /= tempuri;
                }

                for (int i = 0; i < n; i++)
                {
                    if (i == k)
                    {
                        continue;
                    }

                    tempuri = a[i, k];

                    for (j = 0; j < n; j++)
                    {
                        if (j > k)
                        {
                            a[i, j] -= a[k, j] * tempuri;
                        }

                        inverse[i, j] -= inverse[k, j] * tempuri;
                    }
                }
            }

            return inverse;
        }

        /// <summary>
        /// Swaps the rows of a complex number array.
        /// </summary>
        /// <param name="a">The two dimensional array.</param>
        /// <param name="x">The first row to swap.</param>
        /// <param name="y">The second row to swap.</param>
        private static void SwapRows(ComplexNumber[,] a, int x, int y)
        {
            ComplexNumber[] tempuri = new ComplexNumber[a.GetLength(1)];

            for (int i = 0; i < a.GetLength(1); i++)
            {
                tempuri[i] = a[x, i];
            }

            for (int i = 0; i < a.GetLength(1); i++)
            {
                a[x, i] = a[y, i];
            }

            for (int i = 0; i < a.GetLength(1); i++)
            {
                a[y, i] = tempuri[i];
            }
        }
    }
}